// Servidor que simula a los Arduino: se comunican con el cliente de java.
// probar variando el puerto para cada servidor-arduino

//


import java.io.*;
import java.net.*;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Random;
import java.util.Scanner;
import java.util.TimeZone;
import java.util.Vector;

public class Servidor extends Thread {
	
	// VER FUNCIONAMIENTO DE ESTA VERSIÓN EN LA SIGUIENTE DIRECCIÓN:
	// https://sites.google.com/site/jesfmm/project-updates/

	static Vector<Servidor> servidores = new Vector<Servidor>();
	static int base;
	
	static String eventopeso="SET;4;";
	static String eventoRFID="SET;3;";
	static String eventoIR1="SET;1;";
	static String eventoIR2="SET;2;";
	static String respuestatemperatura="SET;6;";
	static String respuestatemperaturayhumedad="SET;5;";
	static String direccionsensortemperatura="00000000;";
	
	int numerodeevento;
	int numeroaleatorio;
	
	float valor;
	int modo;
	float tara;
	static String tipocodigopais = "ISO-FDX-B Animal;1111111;953";
	float temperatura;
	float humedad;
	
	
	public ObjectOutputStream salida;
	public ObjectInputStream entrada;
	public ServerSocket servidor;// = null;
	public Socket conexion;// = null;
	private int contador = 1;
	public int port;
	public String id;
	private String stringSalida = null;
	boolean conexionActiva ;
	boolean peticionDesconexion;
	
	Thread hiloLectura;
	Thread hiloEscritura;
	Random r;

	public Servidor(int puerto)// (int puerto)
	{
		port=puerto;
		id=Integer.toString(puerto);
		//

		hiloLectura=hiloLectura();
		hiloEscritura=hiloEscritura();
		
		this.r=new Random();
		r.setSeed(new Date().getTime());
		
		peticionDesconexion=false;
		conexionActiva=true;
		
		numerodeevento=1;
	
			
	} 
	public Servidor(){

	}
	// fin del constructor de Servidor
	
	// Configurar y ejecutar servidor
	public void ejecutarServidor() {
		
		try {

			// Paso 1: crear objeto ServerSocket.
			
			servidor = new ServerSocket(port, 100);
			
			System.out.println(conexionActiva);

			while (conexionActiva) {
				
				System.out.println("Ejecutar Servidor");

				try {
					esperarConexion(); // Paso 2: esperar conexión.
					obtenerFlujos(); // Paso 3: obtener flujos de entrada y
										// salida.
					procesarConexion(); // Paso 4: procesar conexión.
				}

				// procesar excepción EOFException cuando el cliente cierre la
				// conexión
				catch (EOFException excepcionEOF) {
					System.err.println("El servidor termino la conexion");
				}

				finally {
					System.out.println("Cerrar conexion en ejecutarServidor");
					//cerrarConexion(); // Paso 5: cerrar conexi�n.
					++contador;
				}

			} // Fin while

		} // Fin bloque try

		// Procesar problemas con E/S
		catch (IOException excepcionES) {
			//excepcionES.printStackTrace();
			System.out.println("Conexion cerrada");
		}

	} // Fin del método ejecutarServidor

	// Esperar la conexión, después mostrar información de la conexión
	
	private void esperarConexion() throws IOException {

		if(conexionActiva){
		System.out.println("Esperando una conexion");
		conexion = servidor.accept(); // Permitir al servidor aceptar la conexión
		System.out.println("Conexion " + contador + " recibida de: "
				+ conexion.getInetAddress().getHostName());
		}
	}
	

	//  Obtener flujos para enviar y recibir datos
	private void obtenerFlujos() throws IOException {
		
		// Establecer flujo de salida para los objetos
		try{
		salida = new ObjectOutputStream(conexion.getOutputStream());
		salida.flush(); // Vaciar búfer de salida para enviar información de
						// encabezado

		// Establecer flujo de entrada para los objetos
		entrada = new ObjectInputStream(conexion.getInputStream());
		}catch(IOException e){
			System.out.println("Error de entrada/salida en la obtencion de los flujos de datos");			
		}
		
		if(entrada!=null&&salida!=null)
			System.out.println("Flujos de datos obtenidos");

	}

	// Procesar la conexión con el cliente
	private void procesarConexion() throws IOException {

        hiloEscritura.start();
        hiloLectura.start();
	
		System.out.println("Inicio procesamiento de la conexion");
		
		//while(conexionActiva){
		//	System.out.println("Procesamiento de la conexion");	
		//}
		
		System.out.println("Fin procesamiento de la conexion");

	} // Fin del método procesarConexion

	// Cerrar flujos y socket
	private void cerrarConexion() {

//		try {
//			Thread.sleep(5000);
//		} catch (InterruptedException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
		
		System.out.println("Comienzo cerrarConexion");
		try {
			salida.close();
			entrada.close();
			conexion.close();
			servidor.close();
			System.out.println("Conexion cerrada: "+conexion.isClosed());
			salida=null;
			entrada=null;
			conexion=null;
			conexionActiva=false;

		} catch (IOException excepcionES) {
			excepcionES.printStackTrace();
		}
		System.out.println("Fin cerrarConexion");
	}

	
public Thread hiloLectura(){
	
	
	Thread hiloLectura = new Thread() {
		public void run() throws NullPointerException{ 
			
			String mensaje="data";
			
			System.out.println("Comienzo de hilo de Lectura");
			
			while(conexionActiva){
				
				//System.out.println("Hilo de Lectura");
				
				try {
					mensaje = entrada.readObject().toString();
					// mostrarMensaje( "\n" + mensaje );
					if(mensaje!=null){
					System.out.println("ARDUINO"+"-"+id+"> "+mensaje);}
				}

				catch (ClassNotFoundException excepcionClaseNoEncontrada) {
					System.out
					.println("\nSe recibió un objeto de tipo desconocido");
				} catch (IOException e) {
					// TODO Auto-generated catch block
					//e.printStackTrace();
					System.out.println("Excepcion en la lectura de datos.");
					System.out.println("El cliente se ha desconectado.");
					System.out.println("El servidor va a finalizar.");
					if(conexionActiva==true){
					cerrarConexion();}
					//conexionActiva=false;
				}
				
				
				if (mensaje.startsWith("GET;5")&&salida!=null) {
					
					temperatura=r.nextFloat()*35;
					humedad=r.nextFloat()*100;
					
					stringSalida=respuestatemperaturayhumedad+temperatura+";"+humedad+";"+fecha()+'\n';
		
					
					try {
						salida.writeObject(stringSalida);
						salida.flush();
						System.out.println("ARDUINO"+"-"+id+"> "+"[ARDUINO"+"-"+id+"] " + stringSalida);
					}

					catch (IOException excepcionES) {
						System.err.println("Error de entrada/salida en el hilo de escritura de servidor");
						System.err.println(stringSalida);
					}
				}
				
				if (mensaje.startsWith("GET;6")&&salida!=null) {
					
					temperatura=r.nextFloat()*35;
					
					stringSalida=respuestatemperatura+temperatura+";"+direccionsensortemperatura+fecha()+'\n';

					try {
						salida.writeObject(stringSalida);
						salida.flush();
						System.out.println("ARDUINO"+"-"+id+"> "+"[ARDUINO"+"-"+id+"] " + stringSalida);
					}

					// procesar problemas que pueden ocurrir al enviar el objeto
					catch (IOException excepcionES) {
						System.err.println("Error de entrada/salida en el hilo de escritura de servidor");
						System.err.println(stringSalida);
					}
				}
				
				
			    
			}
		
			System.out.println("Fin del hilo de lectura");
		}
	};

	return hiloLectura;
}

public Thread hiloEscritura(){
	
	Thread hiloEscritura = new Thread() {
		public void run() { 
			
			System.out.println("Comienzo del Hilo de Escritura");
			
			while (conexionActiva) {
				
				numeroaleatorio=r.nextInt(3);
				
				if(numeroaleatorio==0){
					
					valor=r.nextFloat()*200;
					modo=r.nextInt(1);
					tara=r.nextFloat()*5;
					
					stringSalida=eventopeso+Integer.toString(numerodeevento)+";"+valor+";"+modo+";"+tara+";"+fecha()+'\n';
					
				}else if(numeroaleatorio==1){
					
					stringSalida=eventoRFID+Integer.toString(numerodeevento)+";"+tipocodigopais+";"+fecha()+'\n';
					
				}else if(numeroaleatorio==2){
					
					stringSalida=eventoIR1+Integer.toString(numerodeevento)+";"+fecha()+'\n';
					
				}else if(numeroaleatorio==3){
					
					stringSalida=eventoIR2+Integer.toString(numerodeevento)+";"+fecha()+'\n';
					
				}
				
				
				if (salida != null && stringSalida!=null) {

					try {
						salida.writeObject(stringSalida);
						salida.flush();
						System.out.println("ARDUINO"+"-"+id+"> "+"[ARDUINO"+"-"+id+"] " + stringSalida);
					}

					catch (IOException excepcionES) {
						System.err.println("Error de entrada/salida en el hilo de escritura de servidor");
						System.err.println(stringSalida);
					}
				}
				
				try {
					sleep(r.nextInt(14000)+1000); //espera aleatoria entre 1 y 15 segundos
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
				numerodeevento++;
				if(numerodeevento>=1000)
					numerodeevento=1;
			}			
		
		System.out.println("Fin del hilo de escritura");
		}
	};

	return hiloEscritura;
}

//HILO LECTURA DE COMANDOS

static public Thread hiloLecturaComandos() {
	
	//conexionActiva=true;

	Thread hiloLecturaComandos = new Thread() {

		public void run() {

			String cadenaEntrada=null;
			//BufferedReader leerEntrada = new BufferedReader(
					//new InputStreamReader(System.in));

			//System.in.readLine();
		    Scanner sc = new Scanner(System.in);
		    //cadenaEntrada=sc.next();

			while (true) {
				System.out.println("Comandos 1");
				try {
					sleep(200);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				//try {
					// Definimos un flujo de caracteres de entrada:
					// leerEntrada

					// Leemos la entrada, finaliza al pulsar la tecla Entrar
					cadenaEntrada=sc.next();//leerEntrada.readLine();
					System.out.println("Comando introducido: "+cadenaEntrada);
				//} catch (IOException e) {
				//	System.err.println("Error: " + e.getMessage());
				//}
					
					System.out.println("Comandos 2");

				if (cadenaEntrada.startsWith("DIS")) {
					System.out.println("Introducido comando para desconexion");
					//System.out.println(index_n);
					//System.out.println(base);
					cadenaEntrada=sc.next();
					//System.out.println(cadenaEntrada);
					System.out.println("Desconexion con el cliente numero "+(Integer.parseInt(cadenaEntrada)-base));
					servidores.elementAt(Integer.parseInt(cadenaEntrada)-base).conexionActiva=false;
					try {
						sleep(5000);//Esperar que acaben los hilos de lectura y escritura antes de cerrar la conexion.
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					//Comentar lo siguiente para Windows
					servidores.elementAt(Integer.parseInt(cadenaEntrada)-base).cerrarConexion();
					servidores.elementAt(Integer.parseInt(cadenaEntrada)-base).peticionDesconexion=true;
					System.out.println(servidores.elementAt(Integer.parseInt(cadenaEntrada)-base).peticionDesconexion);
					//System.out.println(index_n);
					//System.out.println(Integer.parseInt(cadenaEntrada)-baseindex);
					//index_n++;
					//System.out.println(Integer.parseInt(cadenaEntrada.substring(4, cadenaEntrada.length())));
					//servidores.elementAt(Integer.parseInt(cadenaEntrada.substring(4, cadenaEntrada.length()))-12340-1).conexionActiva=false;
				}
				
				System.out.println("Comandos 3");
				if (cadenaEntrada.startsWith("CON")) {
					System.out.println("Comando introducido para conexión");
					//System.out.println(index_n);
					//System.out.println(baseindex);
					//System.out.println(Integer.parseInt(cadenaEntrada));
					//System.out.println(Integer.parseInt(cadenaEntrada)-baseindex);
					//index_n++;
					//System.out.println(Integer.parseInt(cadenaEntrada.substring(4, cadenaEntrada.length())));
					//aplicacion.servidores.elementAt(Integer.parseInt(cadenaEntrada.substring(4, cadenaEntrada.length()))-12340-1).conexionActiva=true;
					//
					cadenaEntrada=sc.next();
					System.out.println(cadenaEntrada);
					if( Integer.parseInt(cadenaEntrada) == servidores.elementAt(Integer.parseInt(cadenaEntrada)-base).port){
					servidores.elementAt(Integer.parseInt(cadenaEntrada)-base).peticionDesconexion=false;}
					////servidores.setElementAt(new Servidor(Integer.parseInt(cadenaEntrada)),Integer.parseInt(cadenaEntrada)-base);
					////servidores.elementAt(Integer.parseInt(cadenaEntrada)-base).conexionActiva=true;
					//index[Integer.parseInt(cadenaEntrada)-baseindex]=index_n;
	      			//Servidor aplicacion2=new Servidor(Integer.parseInt(cadenaEntrada.substring(4, cadenaEntrada.length())));
	    			System.out.println("Comandos 4");
	    			//System.out.println(index_n);//Integer.parseInt(cadenaEntrada)-12340-1
	      			//aplicacion2.ejecutarServidor();
	      			//Thread hiloEjecucionServidores = hiloEjecucionServidores(Integer.parseInt(cadenaEntrada)-base);
	      			//hiloEjecucionServidores.start();
	    			System.out.println("Comandos 5");
	    			//index_n++;
					//aplicacion2.servidores.elementAt(Integer.parseInt(cadenaEntrada.substring(4, cadenaEntrada.length()))-12340-1).conexionActiva=true;
					//
					
				}
				System.out.println("Comandos 6");

			}

			//
			
		}
		//public void setStringEntrada(String stringEntrada) {
			//this.stringEntrada = stringEntrada;
		//}
//		public void setStringEntrada(String stringEntrada) {
//			this.stringEntrada = stringEntrada;
//		}
		//this.
		//conexion.close();

	};

	return hiloLecturaComandos;
}



//HILO EJECUCION SERVIDORES

static public Thread hiloEjecucionServidores(final int indice) {
	
	Thread hiloEjecucionServidores = new Thread() {

		public void run() {
			
			while(true){
	
			System.out.println("Comienzo HiloEjecucionServidores");
				servidores.elementAt(indice).ejecutarServidor();
			System.out.println("Fin ejecutarServidor");
			
			System.out.println("Se reintentará conexion en 5 segundos.");
			System.out.println(servidores.elementAt(indice).peticionDesconexion);
			try {
				sleep(5000);
			} catch (InterruptedException e) {
			// TODO Auto-generated catch block
				e.printStackTrace();
			}
			while(servidores.elementAt(indice).peticionDesconexion==true){
				
				try {
					sleep(5000);
				} catch (InterruptedException e) {
				// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println("Esperando peticion de conexion");
				
			}

			//System.out.println(indice);
			//System.out.println(base);
			servidores.setElementAt(new Servidor(indice+base),indice);
			
			}
			
		}
	};
	
	return hiloEjecucionServidores;

}

public static String fecha(){
	
	String target_time_zone="GMT-0:00";
	String format="ss;mm;HH;dd;MM;yyyy";
    TimeZone tz = TimeZone.getTimeZone(target_time_zone);
    Date date = Calendar.getInstance().getTime();
    SimpleDateFormat date_format_gmt = new SimpleDateFormat(format);
    date_format_gmt.setTimeZone(tz);
    return date_format_gmt.format(date);
}



	//
	public static void main(String args[]) {
		
		int i=0;

		Thread hiloLecturaComandos = hiloLecturaComandos();
		hiloLecturaComandos.start();
		
		base=Integer.parseInt(args[0]);
		
		while(i<args.length){
		
			servidores.addElement(new Servidor(Integer.parseInt(args[i])));

			Thread hiloEjecucionServidores = hiloEjecucionServidores(i);
			hiloEjecucionServidores.start();
			i++;
	
		
		}
		
	}
	

} // fin de la clase Servidor

